﻿#pragma once

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <stdlib.h>
#include <mutex>
#include <atomic>

#include <boost/algorithm/string.hpp>
#include <jsoncpp/json/json.h>

#include "../Static/Mathf.h"

namespace ns_Util
{
	class BandFrequency
	{
	public:
		static double L1;
		static double L2;
		static double L5;
	};

	class Earth
	{
	public:
		static int LongAxis;		// 长轴
		static int ShortAxis;		// 短轴
		static double f;			// a^2/b^2
		static double Eccentricity; // 第一偏心率
	};

	double BandFrequency::L1 = 1575.42;
	double BandFrequency::L2 = 1227.60;
	double BandFrequency::L5 = 1176.45;

	int Earth::LongAxis = 6378137;
	int Earth::ShortAxis = 6356755;
	double Earth::f = 1.0 / 298.257223563;
	double Earth::Eccentricity = Mathf::Sqrt(2 * f - f * f);

	enum RunStateType
	{
		Running,
		RunStop,
		RunError
	};

	enum LogStateType
	{
		LogSuccess,
		LogWait,
		LogError
	};

	enum ErrorType
	{

	};

	enum LogInfType
	{
		NormalLog,
		WarnLog,
		ErrorLog,

	};

	enum DataSourceType
	{
		SelfEncode,
		OfficialFile,
	};

	enum FileType
	{
		RINEX2x,
		RINEX3x,
	};

	enum GNSSType
	{
		BDS = 0,
		GPS,
		GLONASS,
		Galileo,
		QZSS,
		IRNSS,
		SBSA,
		DefaultGNSSType
	};

	enum FileReadStateType
	{
		FileReadSuccess,
		FileReadDefault,
		FileReadIgnore,
		FileOpenDefault,
	};

	enum DecodeStateType
	{
		DecodeSuccess,
		DecodeError,
		DecodeStop,
	};

	enum OutputStateType
	{
		OutputSuccess,
		OutputError,
		OutputIgnore,
		OutputFail
	};

	enum OutputTargetType
	{
		OutputToShell,
		OutputToFile,
		OutputToSocket
	};

	enum HandleStateType
	{
		HandleSuccess,
		HandleBusy,
		HandleError
	};
}

namespace ns_Util
{
	class GNSSTypeUtil
	{
	public:
		static GNSSType GetGNSSTypeByMark(char mark)
		{
			const char letters = mark;

			if (letters == 'G')
				return GNSSType::GPS;
			else if (letters == 'C')
				return GNSSType::BDS;
			else if (letters == 'E')
				return GNSSType::Galileo;
			else if (letters == 'R')
				return GNSSType::GLONASS;
			else if (letters == 'I')
				return GNSSType::IRNSS;
			else if (letters == 'J')
				return GNSSType::QZSS;
			else if (letters == 'S')
				return GNSSType::SBSA;

			return GNSSType::DefaultGNSSType;
		}

		static GNSSType GetGNSSTypeByMark(const std::string &name)
		{
			if (name.empty())
				return GNSSType::DefaultGNSSType;

			char letters = name[0];

			if (letters == 'G')
				return GNSSType::GPS;
			else if (letters == 'C')
				return GNSSType::BDS;
			else if (letters == 'E')
				return GNSSType::Galileo;
			else if (letters == 'R')
				return GNSSType::GLONASS;
			else if (letters == 'I')
				return GNSSType::IRNSS;
			else if (letters == 'J')
				return GNSSType::QZSS;
			else if (letters == 'S')
				return GNSSType::SBSA;

			return GNSSType::DefaultGNSSType;
		}

		static GNSSType GetGNSSTypeByName(const std::string &name)
		{
			if (name.size() < 2)
				return GNSSType::DefaultGNSSType;

			std::string letters = name.substr(0, 2);

			if (letters == "GP")
				return GNSSType::GPS;
			else if (letters == "BD")
				return GNSSType::BDS;
			else if (letters == "GA")
				return GNSSType::Galileo;
			else if (letters == "GL")
				return GNSSType::GLONASS;
			else if (letters == "IR")
				return GNSSType::IRNSS;
			else if (letters == "QZ")
				return GNSSType::QZSS;

			return GNSSType::DefaultGNSSType;
		}
	};

	class GNSSNameUtil
	{
	public:
		static std::string GetGNSSNameByType(GNSSType type)
		{
			switch (type)
			{
			case GNSSType::BDS:
				return "BDS";
				break;
			case GNSSType::GPS:
				return "GPS";
				break;
			case GNSSType::GLONASS:
				return "GLONASS";
				break;
			case GNSSType::Galileo:
				return "Galileo";
				break;
			case GNSSType::IRNSS:
				return "IRNSS";
				break;
			case GNSSType::QZSS:
				return "QZSS";
				break;
			case GNSSType::SBSA:
				return "SBSA";
				break;
			}

			return "UnkownGNSSType";
		}
	};

	class BandUtil
	{
	public:
		static double GetLambda(GNSSType type, int band)
		{
			double ans = 0;
			if (type == GNSSType::GPS)
			{
				switch (band)
				{
				case 1:
					ans = 0.1904;
					break;
				case 2:
					ans = 0.2444;
					break;
				case 5:
					ans = 0.255;
					break;
				default:
					break;
				}
			}
			else if (type == GNSSType::BDS)
			{
				switch (band)
				{
				case 1:
					ans = 0.1904;
					break;
				case 2:
					ans = 0.1922;
					break;
				case 5:
					ans = 0.255;
					break;
				case 6:
					ans = 0.2365;
					break;
				case 7:
					ans = 0.2485;
					break;
				case 8:
					ans = 0.2517;
					break;
				case 9:
					ans = 0.2485;
					break;
				default:
					break;
				}
			}

			return ans;
		}
	};

	class TimeUtil
	{
	public:
		static void GetCurTimeInYear(std::string *output)
		{
			if (output == nullptr)
				return;

			timeval out;
			gettimeofday(&out, nullptr);

			tm *curTime = localtime(&out.tv_sec);
			std::string str;
			str += std::to_string(curTime->tm_year) + ' ';
			str += std::to_string(curTime->tm_mon) + ' ';
			str += std::to_string(curTime->tm_mday) + ' ';
			str += std::to_string(curTime->tm_hour) + ':' + std::to_string(curTime->tm_min) + ':' + std::to_string(curTime->tm_sec);

			*output = str;
		}

		static void GetCurTimeInDay(std::string *output)
		{
			if (output == nullptr)
				return;

			timeval out;
			gettimeofday(&out, nullptr);

			tm *curTime = localtime(&out.tv_sec);
			std::string str;
			str += std::to_string(curTime->tm_hour) + ':' + std::to_string(curTime->tm_min) + ':' + std::to_string(curTime->tm_sec);

			*output = str;
		}

		static void GetCurTimeMs(std::string *output)
		{
			if (output == nullptr)
				return;

			timeval out;
			gettimeofday(&out, nullptr);

			*output = std::to_string(out.tv_usec);
		}
	};

	class FileUtil
	{
	public:
		static bool IsFileExist(const std::string &pathName)
		{
			struct stat st;
			if (stat(pathName.c_str(), &st) == 0)
			{
				return true;
			}

			return false;
		}

		static bool WriteToFile(const std::string &target, const std::string &content)
		{
			std::ofstream out(target);
			if (!out.is_open())
			{
				return false;
			}
			out.write(content.c_str(), content.size());
			out.close();
			return true;
		}
		static bool ReadFromFile(const std::string &filePath, std::string *content, bool keep = false)
		{
			(*content).clear();

			std::ifstream in(filePath);
			if (!in.is_open())
			{
				return false;
			}
			std::string line;
			// getline:不保存行分割符,有些时候需要保留\n,
			while (std::getline(in, line))
			{
				(*content) += line;
				(*content) += (keep ? "\n" : "");
			}
			in.close();
			return true;
		}

		static bool RemoveFilesInDirectory(const std::string &dirPath)
		{
			DIR *dir;
			struct dirent *entry;

			dir = opendir(dirPath.c_str());
			if (dir == NULL)
			{
				return false;
			}

			while ((entry = readdir(dir)) != NULL)
			{
				if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
				{
					continue;
				}

				char path[1024];
				snprintf(path, sizeof(path), "%s/%s", dirPath.c_str(), entry->d_name);

				unlink(path);
			}

			closedir(dir);
			return true;
		}
	};

	const std::string defaultPath = "./temp/";

	class PathUtil
	{
	public:
		static std::string Record(const std::string &fileName, GNSSType type, int serial)
		{
			std::string path = Dir(fileName);
			MakeDir(path);

			return path + '/' + GNSSNameUtil::GetGNSSNameByType(type) + '_' + std::to_string(serial) + ".record";
		}

		static std::string ObsRecord(const std::string &fileName, time_t timeStamp)
		{
			std::string path = Dir(fileName);
			MakeDir(path);

			return path + '/' + std::to_string(timeStamp) + ".record";
		}

		static std::string Header(const std::string &fileName, GNSSType type)
		{
			std::string path = Dir(fileName);
			MakeDir(path);

			return path + '/' + GNSSNameUtil::GetGNSSNameByType(type) + ".header";
		}

		static std::string Src(const std::string &fileName)
		{
			std::string path = defaultPath + "srcdir";
			MakeDir(path);

			return path + '/' + fileName + ".src";
		}

		static std::string Dir(const std::string &fileName)
		{
			return defaultPath + fileName + ".dir";
		}

	private:
		static bool MakeDir(const std::string &path)
		{
			if (FileUtil::IsFileExist(path))
			{
				return false;
			}
			mkdir(path.c_str(), 0755);
			return true;
		}
	};

	class JsonUtil
	{
	public:
		static std::string ValueToString(const Json::Value &value)
		{
			Json::FastWriter writer;
			return writer.write(value);
		}

		static Json::Value StringToValue(const std::string &json)
		{
			Json::Reader reader;
			Json::Value ans;
			reader.parse(json, ans);
			return ans;
		}
	};
}